/**
 * 	This file is part of GMGraphLib.
 * 
 * 	GMGraphLib is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  GMGraphLib is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with GMGraphLib.  If not, see <http://www.gnu.org/licenses/>.
*/
package br.mendonca.gmgraphlib;

import java.util.LinkedList;
import java.util.List;

import br.mendonca.gmgraphlib.exception.InvalidEdgeException;

/**
 * @author Gabriel Mendonça - gabrielgmendonca@gmail.com
 *
 */
public abstract class Graph {

	public static final int ADJACENCY_LIST = 10;
	public static final int ADJACENCY_MATRIX = 20;
	
	private VertexList vertexList;	
	private EdgeList edgeList;
	
	public boolean emptyEdgeList = true;
	
	public static Graph getInstance(String type, int numberOfVertices, int dataStructure) {
		if (type.equals(DirectedGraph.TYPE)) {
			return new DirectedGraph(numberOfVertices, dataStructure);
		}
		return null;
	}
	
	public Graph(int numberOfVertices, int dataStructure) {
		vertexList = new VertexList(numberOfVertices);
		createEdgeList(numberOfVertices, dataStructure);		
	}

	/**
	 * Adds an edge to the graph.
	 * @param v1 first vertex connected to the edge
	 * @param v2 second vertex connected to the edge
	 */
	public void addEdge(int v1, int v2) throws InvalidEdgeException {
		if (v1 <= vertexList.size() && v2 <= vertexList.size()) {
			edgeList.addEdge(v1, v2);
		} else {
			throw new InvalidEdgeException();
		}
	}	

	/**
	 * Adds a vertex to the graph.
	 */
	@Deprecated
	public void addVertex() {
		vertexList.add(new Vertex());
		edgeList.addNewVertex(vertexList.size());
	}

	/**
	 * @param vertexList the vertexList to set
	 */
	public void setVertexList(VertexList vertexList) {
		this.vertexList = vertexList;
	}

	/**
	 * @return the vertexList
	 */
	public List<Vertex> getVertexList() {
		return vertexList;
	}

	/**
	 * @return the edgeList
	 */
	public EdgeList getEdgeList() {
		return edgeList;
	}

	/**
	 * @param edgeList the edgeList to set
	 */
	public void setEdgeList(EdgeList edgeList) {
		this.edgeList = edgeList;
	}

	/**
	 * When called, returns a list with the neighbors of the
	 * vertex with the given index.
	 * 
	 * @param vertexIndex index of the vertex
	 * @return a list with the neighbors of the vertex
	 */
	public List<Vertex> getNeighbors(int vertexIndex) {
		List<Vertex> neighbors = new LinkedList<Vertex>();
		for (Integer neighborIndex : edgeList.getNeighborsIndex(vertexIndex)) {
			neighbors.add(vertexList.get(neighborIndex - 1));
		}
		
		return neighbors;		
	}

	private void createEdgeList(int numberOfVertices, int dataStructure) {
		switch (dataStructure) {
			case ADJACENCY_LIST:
			edgeList = new AdjacencyListEdges(numberOfVertices);
			break;
			
		case ADJACENCY_MATRIX:
			edgeList = new AdjacencyMatrixEdges(numberOfVertices);
			break;			
		}
	}

}
